Skip to content

Fix TypeError when timesteps list has a gap in blending.steps.forecast - #560

Open
Yuxiang-Ren-HUB wants to merge 1 commit into
pySTEPS:masterfrom
Yuxiang-Ren-HUB:fix-empty-subtimesteps-nonetype
Open

Fix TypeError when timesteps list has a gap in blending.steps.forecast#560
Yuxiang-Ren-HUB wants to merge 1 commit into
pySTEPS:masterfrom
Yuxiang-Ren-HUB:fix-empty-subtimesteps-nonetype

Conversation

@Yuxiang-Ren-HUB

Copy link
Copy Markdown

Problem

Calling pysteps.blending.steps.forecast() with a sparse timesteps list that has a gap (e.g. [1, 3], skipping 2) raises:

TypeError: 'NoneType' object is not iterable

at self.__state.final_blended_forecast[j].extend(...) inside __blended_nowcast_main_loop.

Root cause

In StepsBlendingNowcaster.__blended_nowcast_main_loop, worker(j) builds final_blended_forecast_single_member inside for t_sub in self.__state.subtimesteps:, but the line that stores it into final_blended_forecast_all_members_one_timestep[j] is indented one level too deep — inside that same for loop instead of after it:

for t_sub in self.__state.subtimesteps:
    if t_sub > 0:
        ...
        final_blended_forecast_single_member = (
            self.__post_process_output(...)
        )

    final_blended_forecast_all_members_one_timestep[j] = (   # <- inside the for loop
        final_blended_forecast_single_member
    )

When a requested timesteps list has a gap, nowcast_utils.binned_timesteps produces an empty bin for the skipped native step, so self.__state.subtimesteps == [] at that outer step. The for t_sub in []: loop body then never runs, so the assignment never happens, and final_blended_forecast_all_members_one_timestep[j] is left at its initial value of None. That later gets passed to .extend(None).

Note the two existing list-timesteps cases in test_blending_steps.py don't actually exercise this: [1, 2, 3] has no gap, and the other [1, 3] case uses zero_radar=True, zero_nwp=True, which takes an early-exit shortcut that skips the main loop entirely.

Fix

Dedent the assignment one level so it runs once per ensemble member after the subtimesteps loop finishes, regardless of whether that loop ran zero, one, or more iterations — matching the intent of "whatever this member produced at this outer step, even if that's nothing."

Testing

  • Added a regression test: timesteps=[1, 3] with non-zero radar/NWP precipitation (the two existing conditions needed to hit this bug at once).
  • Reproduced the crash directly against the pre-fix code, confirmed the fix resolves it with no other behavior change.
  • Full pysteps/tests/test_blending_steps.py suite: 59 passed (58 existing + 1 new).
  • black --check passes with no formatting changes needed.

final_blended_forecast_all_members_one_timestep[j] was assigned inside
the `for t_sub in subtimesteps:` loop, so when subtimesteps is empty
(a bin with no requested output, e.g. the skipped step 2 in
timesteps=[1, 3]) the assignment never ran and the value stayed at its
initial None. That later hit
`self.__state.final_blended_forecast[j].extend(None)`, raising
"TypeError: 'NoneType' object is not iterable".

Dedent the assignment one level so it runs once per member after the
subtimesteps loop completes, whether or not that loop ran at all.

Added a regression test (timesteps=[1, 3] with non-zero radar/NWP
precip) -- the two existing list-timesteps test cases didn't actually
exercise this path: [1, 2, 3] has no gap, and the other [1, 3] case
uses zero precipitation, which takes an early-exit shortcut that skips
the main loop entirely.
@Yuxiang-Ren-HUB

Copy link
Copy Markdown
Author

Hi maintainers, just checking in on this PR — it's been open for a few weeks and I noticed the CI test workflow doesn't seem to have run yet (only the Codacy check shows up). Let me know if there's anything needed on my end, or if it just needs someone to approve the workflow run. Happy to make any changes if needed. Thanks for your time!

@dnerini

dnerini commented Aug 6, 2026

Copy link
Copy Markdown
Member

Hi @Yuxiang-Ren-HUB, thanks for the nice fix! It looks good to me!

@RubenImhoff do you want to have a quick look too?

@dnerini
dnerini requested a review from RubenImhoff August 6, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants